#ifndef __SYMBOLTABLE_HPP__
#define __SYMBOLTABLE_HPP__
#include<map>
#include<string>
#include<vector>
#include<iostream>
#include<algorithm>
#include<fstream>
#include<functional>
using namespace std;

enum TypeKind{
    BOOL_TYPE = 1,

    INT8_TYPE,
    UINT8_TYPE,

    INT16_TYPE,
    UINT16_TYPE,

    INT32_TYPE,
    UINT32_TYPE,

    INT64_TYPE,
    UINT64_TYPE,

    DOUBLE_TYPE,
    POINTER_TYPE,
    CLASS_TYPE = -1
};

struct DataMemberInfo;
struct FunctionMemberInfo;
struct EntryInfo;

struct TypeInfo
{
    TypeInfo(){}
    TypeInfo(const string& typeName,int size = 0,TypeKind typeKind = TypeKind::CLASS_TYPE):
        typeName(typeName),
        size(size),
        typeKind(typeKind)
        {}
    void setSize(int size){
        this->size = size;
    }
    TypeKind typeKind;
    string typeName;      
    int size;             // 类型大小
    vector<DataMemberInfo> dataMember;
    vector<FunctionMemberInfo> functionMember;
    vector<EntryInfo>       entryInfo;
};

enum AccessModifier{
    FUNCTION_LOCAL,
    PUBLIC,
    PRIVATE
};

struct Entity{
    TypeInfo*       classScope;
    AccessModifier accessModifier;
    TypeInfo*   typeInfo;
    string  dataMemberName;
    bool    isStatic;
    int     size;
    int     offset;
    Entity(){}
    Entity(bool isStatic,AccessModifier accessModifier,TypeInfo* typeInfo,const string& dataMemberName,int offset):
        classScope(nullptr),
        isStatic(isStatic),
        accessModifier(accessModifier),
        typeInfo(typeInfo),
        dataMemberName(dataMemberName),
        offset(offset),
        size(typeInfo->size)
        {}
    void setOffset(int offset){
        this->offset = offset;
    }
    void setClassScope(TypeInfo* classScope){
        this->classScope = classScope;
    }
    TypeInfo* getClassScope(){
        return this->classScope;
    }
};

struct DataMemberInfo : public Entity{
    DataMemberInfo(){}
    DataMemberInfo(bool isStatic,AccessModifier accessModifier,TypeInfo* typeInfo,const string& dataMemberName,int offset):
        Entity(isStatic,accessModifier,typeInfo,dataMemberName,offset)
        {}
};

struct FormalParameterInfo : public Entity{
    FormalParameterInfo(){}
    FormalParameterInfo(TypeInfo* typeInfo,const string& formalParameterName):
        Entity(false,AccessModifier::FUNCTION_LOCAL,typeInfo,formalParameterName,-1)
        {}
};

struct VariableInfo : public Entity{
    VariableInfo(){}

    VariableInfo(bool isStatic,TypeInfo* typeInfo,const string& dataMemberName,int offset):
        Entity(isStatic,AccessModifier::FUNCTION_LOCAL,typeInfo,dataMemberName,offset)
        {}
};



struct FunctionMemberInfo{
    FunctionMemberInfo(){}
    FunctionMemberInfo(const string& functionMemberName,bool isStatic = false):functionMemberName(functionMemberName),isStatic(isStatic){}
    TypeInfo*       classScope;
    bool isStatic;
    AccessModifier accessModifier;  
    string                      functionMemberName;
    TypeInfo*                   returnType; // 返回类型
    vector<FormalParameterInfo>   parameters; // 形参列表
    vector<VariableInfo>   variables;  // 局部变量
                                            // 作用域，是谁的成员函数
    int local_variable_block_size;          // 函数内声明的变量大小
    int parameter_block_size;               // 参数大小

    void setClassScope(TypeInfo* classScope){
        this->classScope = classScope;
    }
    TypeInfo* getClassScope(){
        return this->classScope;
    }
};

struct EntryInfo : public FunctionMemberInfo{
    EntryInfo():FunctionMemberInfo("main"){};
};


#define STACK_BYTES_ALIGNMENT   4
#define PARAMETER_INIT_OFFSET   8
struct SymbolTable{
public:
    SymbolTable(){
        classTable.push_back(TypeInfo("bool",1,BOOL_TYPE));

        classTable.push_back(TypeInfo("int8",1,TypeKind::INT8_TYPE));
        classTable.push_back(TypeInfo("int16",2,TypeKind::INT16_TYPE));
        classTable.push_back(TypeInfo("int32",4,TypeKind::INT32_TYPE));
        classTable.push_back(TypeInfo("int64",8,TypeKind::INT64_TYPE));

        classTable.push_back(TypeInfo("uint8",1,TypeKind::UINT8_TYPE));
        classTable.push_back(TypeInfo("uint16",2,TypeKind::UINT16_TYPE));
        classTable.push_back(TypeInfo("uint32",4,TypeKind::UINT32_TYPE));
        classTable.push_back(TypeInfo("uint64",8,TypeKind::UINT64_TYPE));

        currentClassScopeIndex = classTable.size();
        nextFormalParameterOffset = PARAMETER_INIT_OFFSET;  // 但8留给this指针
    }
public:
    bool isValidIdentifier(const string& identifier,bool isInFunctionScope = false){
        if (getTypeInfo(identifier) == nullptr)
            return false;

        TypeInfo* classScope = getCurrentClassScope();
        if (classScope != nullptr){
            if (getDataMember(classScope,identifier) != nullptr)
                return false;
            if (getFunctionMemberInfo(classScope,identifier) != nullptr)
                return false;
        }


        /*if (data_member_table.count(identifier) != 0)
            return false;
        if (function_member_table.count(identifier) != 0)
            return false;
        if (isInFunctionScope){
            if (formalParametersTable.count(identifier) != 0)
                return false;
            if (local_variable_table.count(identifier) != 0)
                return false;
        }*/

        return true;
    }
    void addTypeInfo(const string& class_name){
        classTable.push_back(TypeInfo(class_name));
        classTable.back().dataMember = move(data_member_table);
        classTable.back().functionMember = move(function_member_table);
        classTable.back().setSize(nextDataMemberOffset);
        
        for (auto& data : classTable.back().dataMember)
            data.setClassScope(&classTable.back());
        
        nextDataMemberOffset = 0;
    }
    void addDataMemberInfo(bool isStatic,AccessModifier accessModifier,TypeInfo* typeInfo,const string& dataMemberName){
        data_member_table.push_back(DataMemberInfo(isStatic,accessModifier,typeInfo,dataMemberName,nextDataMemberOffset));
        nextDataMemberOffset += typeInfo->size;
    }

    void addFormalParameter(TypeInfo* typeInfo,const string& formalParameterName){
        // 从12开始分配,8分配给this指针   越靠右的参数,值越大,也就是从右往左开始入栈
        //nextFormalParameterOffset = (nextFormalParameterOffset + typeInfo->size + STACK_BYTES_ALIGNMENT - 1)/STACK_BYTES_ALIGNMENT*STACK_BYTES_ALIGNMENT;
        formalParametersTable.push_back(FormalParameterInfo(typeInfo,formalParameterName));

    }
    void addLocalVariable(bool isStatic,TypeInfo* typeInfo,const string& variableName){

        nextVariableOffset = (nextVariableOffset + typeInfo->size + STACK_BYTES_ALIGNMENT - 1)/STACK_BYTES_ALIGNMENT*STACK_BYTES_ALIGNMENT;
        local_variable_table.push_back(VariableInfo(isStatic,typeInfo,variableName,-nextVariableOffset));
    }

    void updateEntryInfo(const string& class_name){
        /*TypeInfo* typeInfo = getTypeInfo(class_name);
        entryInfo.local_variable_block_size = nextVariableOffset;
        entryInfo.returnType = getTypeInfo("int32");
        entryInfo.parameters = move(formalParametersTable);
        entryInfo.variables   = move(local_variable_table);
        typeInfo->entryInfo.push_back(move(entryInfo));*/

        nextVariableOffset = 0;
    }
    void updateEntryInfo(){
        entryInfo.returnType = getTypeInfo("int32");
        entryInfo.local_variable_block_size = nextVariableOffset;
        entryInfo.parameters = move(formalParametersTable);
        entryInfo.variables   = move(local_variable_table);
        nextVariableOffset = 0;
    }

    void addFunctionMemberInfo(AccessModifier accessModifier, TypeInfo* returnType,const string& fun_name){
        function_member_table.push_back(FunctionMemberInfo(fun_name));
        function_member_table.back().accessModifier = accessModifier;
        function_member_table.back().returnType = returnType;
        function_member_table.back().local_variable_block_size = nextVariableOffset;
        function_member_table.back().parameter_block_size = nextFormalParameterOffset - PARAMETER_INIT_OFFSET;
        function_member_table.back().parameters  = move(formalParametersTable);
        function_member_table.back().variables   = move(local_variable_table);

        reverse(function_member_table.back().parameters.begin(),function_member_table.back().parameters.end());
        nextFormalParameterOffset = PARAMETER_INIT_OFFSET;
        for (auto& param : function_member_table.back().parameters){
            nextFormalParameterOffset = (nextFormalParameterOffset + param.size + STACK_BYTES_ALIGNMENT - 1)/STACK_BYTES_ALIGNMENT*STACK_BYTES_ALIGNMENT;
            param.setOffset(nextFormalParameterOffset);
        }

        nextVariableOffset = 0;
    }

    void updateFunctionMemberInfo(const string& class_name){
        TypeInfo* typeInfo = getTypeInfo(class_name);
        typeInfo->functionMember = move(function_member_table);

        for (auto& function : typeInfo->functionMember)
            function.setClassScope(typeInfo);
    }


    bool checkDataMember(){
        map<string,bool> typeName;
        map<string,bool> dataMemberName;
        bool flag = true;

        for (auto& typeInfo : classTable){
            if (typeName.count(typeInfo.typeName) == 0)
                typeName[typeInfo.typeName] = true;
            else {
                printf("错误：类%s重复定义\n",typeInfo.typeName.data());
                flag = false;
            }

            dataMemberName.clear();
            for (auto& dataMemberInfo : typeInfo.dataMember){
                if (dataMemberInfo.dataMemberName == typeInfo.typeName){
                    printf("错误：成员变量%s名字不可与所属类类名相同\n",dataMemberInfo.dataMemberName.data());
                    flag = false;
                    continue;
                }
                if (dataMemberName.count(dataMemberInfo.dataMemberName) == 0)
                    dataMemberName[dataMemberInfo.dataMemberName] = true;
                else {
                    printf("成员变量%s重复定义\n",dataMemberInfo.dataMemberName.data());
                    flag = false;
                }
            }

        }
        
        return flag;
    }


    bool checkFunctionMember(){
        map<string,bool> typeName;
        map<string,bool> dataMemberName;
        map<string,bool> functionName;

        bool flag = true;
        for (auto& typeInfo : classTable){
            if (typeName.count(typeInfo.typeName) == 0)
                typeName[typeInfo.typeName] = true;
            else {
                printf("类%s重复定义\n",typeInfo.typeName.data());
                flag = false;
            }

            dataMemberName.clear();
            for (auto& dataMemberInfo : typeInfo.dataMember)
                dataMemberName[dataMemberInfo.dataMemberName] = true;
            

            functionName.clear();
            for (auto& functionMemberInfo : typeInfo.functionMember){
                if (functionMemberInfo.functionMemberName == typeInfo.typeName){
                    printf("错误：成员函数%s名字不可与所属类类名相同\n",functionMemberInfo.functionMemberName.data());
                    flag = false;
                    continue;
                }

                if (dataMemberName.count(functionMemberInfo.functionMemberName) != 0){
                    printf("错误：%s定义冲突，存在成员变量、成员函数两个定义\n",functionMemberInfo.functionMemberName.data());
                    flag = false;
                }
                else if (functionName.count(functionMemberInfo.functionMemberName) == 0)
                    dataMemberName[functionMemberInfo.functionMemberName] = true;
                else {
                    printf("成员函数%s重复定义\n",functionMemberInfo.functionMemberName.data());
                    flag = false;
                }
            }
            return flag;
        }
    }



    void showDataMember(ofstream& of){
        static char buffer[1024] = {};
        for (auto& cls : classTable){
            sprintf(buffer,"---------------------------------------------------------------\n");
            of << (char*)buffer;
            sprintf(buffer,"| class: %-53s|\n",cls.typeName.data());
            of << (char*)buffer;
            sprintf(buffer,"---------------------------------------------------------------\n");
            of << (char*)buffer;
            //sprintf(buffer,"%s：\n",cls.typeName.data());
            //sprintf(buffer,"\t数据成员：\n");

            for (auto& dataMember : cls.dataMember){
                string staticStr, accessStr, typeStr, dataStr,sizeStr, offsetStr;
                staticStr = dataMember.isStatic ? "static" : "non-static";
                if (dataMember.accessModifier == AccessModifier::PUBLIC)    accessStr = "public";
                else if (dataMember.accessModifier == AccessModifier::PRIVATE) accessStr = "private";
                typeStr = dataMember.typeInfo->typeName;
                dataStr = dataMember.dataMemberName;
                sizeStr = "size:" + to_string(dataMember.size);
                offsetStr = "offset:" + to_string(dataMember.offset);
                sprintf(buffer,"| %-12s%-9s%-8s%-13s%-8s%-10s|\n",staticStr.data(),accessStr.data(),typeStr.data(),dataStr.data(),sizeStr.data(),offsetStr.data());
                of << (char*)buffer;
            }
            sprintf(buffer,"---------------------------------------------------------------\n\n\n");
            of << (char*)buffer;
        }
    }

    void showFunctionMember(ofstream& of){
        static char buffer[1024] = {};
        for (auto& cls : classTable){
            sprintf(buffer,"---------------------------------------------------------------\n");
            of << (char*)buffer;
            sprintf(buffer,"| class: %-53s|\n",cls.typeName.data());
            of << (char*)buffer;
            sprintf(buffer,"---------------------------------------------------------------\n");
            of << (char*)buffer;

            for (auto& fun : cls.functionMember){
                string staticStr, accessStr, typeStr, funStr;
                staticStr = fun.isStatic ? "static" : "non-static";
                if (fun.accessModifier == AccessModifier::PUBLIC)    accessStr = "public";
                else if (fun.accessModifier == AccessModifier::PRIVATE) accessStr = "private";
                typeStr = fun.returnType->typeName;
                funStr = fun.functionMemberName;

                sprintf(buffer,"| %-12s%-9s%-8s%-13s (",staticStr.data(),accessStr.data(),typeStr.data(),funStr.data());
                of << (char*)buffer;


                for (auto& param : fun.parameters){
                    sprintf(buffer," %s",param.typeInfo->typeName.data());
                    of << (char*)buffer;
                }

                sprintf(buffer," )\n");
                of << (char*)buffer;
            }

            sprintf(buffer,"---------------------------------------------------------------\n\n\n");
            of << (char*)buffer;



        }
    }
public:
    TypeInfo* getTypeInfo(const string& typeName){
        auto cmp = [&typeName](const TypeInfo& typeInfo)->bool{return typeInfo.typeName == typeName;};
        auto result = find_if(classTable.begin(),classTable.end(),cmp);
        if (result == classTable.end())
            return nullptr;
        return &(*result);
    }

    FunctionMemberInfo* getFunctionMemberInfo(TypeInfo* typeInfo,const string& functionMemberName){
        auto cmp = [&functionMemberName](FunctionMemberInfo& functionMemberInfo)->bool{return functionMemberInfo.functionMemberName == functionMemberName;};
        auto result = find_if(typeInfo->functionMember.begin(),typeInfo->functionMember.end(),cmp);
        if (result == typeInfo->functionMember.end())
            return nullptr;
        return &(*result);
    }

    Entity* getDataMember(TypeInfo* typeInfo, const string& dataMemberName){
        auto cmp = [&dataMemberName](DataMemberInfo& dataMemberInfo)->bool{return dataMemberInfo.dataMemberName == dataMemberName;};
        auto result = find_if(typeInfo->dataMember.begin(),typeInfo->dataMember.end(),cmp);
        if (result == typeInfo->dataMember.end())
            return nullptr;
        return &(*result);
    }


    Entity* getParameter(FunctionMemberInfo* functionMemberInfo,const string& paramName){
        auto cmp = [&paramName](FormalParameterInfo& formalParameterInfo)->bool{return formalParameterInfo.dataMemberName == paramName;};
        auto result = find_if(functionMemberInfo->parameters.begin(),functionMemberInfo->parameters.end(),cmp);
        if (result == functionMemberInfo->parameters.end())
            return nullptr;
        return &(*result);
    }
    
    // 函数内定义的局部变量
    Entity* getLocalVariable(FunctionMemberInfo* functionMemberInfo,const string& variable_name){
        auto cmp = [&variable_name](VariableInfo& variableInfo)->bool{return variableInfo.dataMemberName == variable_name;};
        auto result = find_if(functionMemberInfo->variables.begin(),functionMemberInfo->variables.end(),cmp);
        if (result == functionMemberInfo->variables.end())
            return nullptr;
        return &(*result);
    }

    void setCurrentClassScope(const string& typeName){
        auto cmp = [&typeName](const TypeInfo& typeInfo)->bool{return typeInfo.typeName == typeName;};
        auto result = find_if(classTable.begin(),classTable.end(),cmp);
        currentClassScopeIndex = result - classTable.begin();
    }

    void updateCurrentClassScope(){
        if (currentClassScopeIndex < classTable.size())
            ++currentClassScopeIndex >= classTable.size();
    }

    void set_current_function_scope(const string& functionName){
        if (functionName == "main")
            current_function_scope = getEntryInfo();
        else
            current_function_scope = getFunctionMemberInfo(getCurrentClassScope(),functionName);
    }





    

    FunctionMemberInfo* getFunctionMemberInfo(const string& functionName){
        return getFunctionMemberInfo(getCurrentClassScope(),functionName);
    }
    EntryInfo* getEntryInfo(){
        return &entryInfo;
    }
    TypeInfo* getCurrentClassScope(){
        //if (current_function_scope->functionMemberName == "main")
            //return nullptr;
        return &classTable[currentClassScopeIndex];
    }
    FunctionMemberInfo* getCurrentFunctionScope(){
        return current_function_scope;
    }
private:
    int     currentClassScopeIndex;
    FunctionMemberInfo*  current_function_scope;
private:
    vector<DataMemberInfo> data_member_table;
    vector<FunctionMemberInfo> function_member_table;
    vector<FormalParameterInfo>formalParametersTable;         // 形参
    vector<VariableInfo> local_variable_table;
    int nextVariableOffset;
    int nextDataMemberOffset;
    int nextFormalParameterOffset;
    EntryInfo   entryInfo;
private:
    vector<TypeInfo> classTable;
};
inline SymbolTable symbolTable;











#endif